/***************************************************************************

 This source file is part of OGREBULLET
 (Object-oriented Graphics Rendering Engine Bullet Wrapper)
 For the latest info, see http://www.ogre3d.org/phpBB2addons/viewforum.php?f=10

 Copyright (c) 2007 tuan.kuranes@gmail.com (Use it Freely, even Statically, but have to contribute any changes)



 This source file is not LGPL, it's public source code that you can reuse.
 -----------------------------------------------------------------------------*/
#include "StemLifeScene.h"

#include "OgreBulletDynamicsWorld.h"
#include "OgreBulletDynamicsRigidBody.h"
#include "Debug/OgreBulletCollisionsDebugDrawer.h"
#include "Shapes/OgreBulletCollisionsBoxShape.h"
#include "Shapes/OgreBulletCollisionsSphereShape.h"
#include "Cell.h"
#include <BioEntity.h>
#include <iostream>
#include "OgreBulletCollisionsShape.h"
#include "Ogre.h"

using namespace Ogre;
using namespace std;
const float gStaticBodyRestitution = 0.1f;
const float gStaticBodyFriction = 0.8f;

const float gDynamicBodyRestitution = 0.6f;
const float gDynamicBodyFriction = 0.6f;
const float gDynamicBodyMass = 1.0f;

const Ogre::Vector3 gCubeBodyBounds = Ogre::Vector3(1, 1, 1);
const Ogre::Vector3 gCylinderBodyBounds = Ogre::Vector3(0.5, 1, 0.5);
const Ogre::Vector3 gConeBodyBounds = Ogre::Vector3(1, 1, 1);
const Real gSphereBodyBounds = 1.0f;

#if !(OGRE_VERSION <  ((1 << 16) | (3 << 8) | 0))
using namespace OIS;
#endif

using namespace Ogre;
using namespace OgreBulletCollisions;
using namespace OgreBulletDynamics;
using std::endl;

// -------------------------------------------------------------------------
void StemLifeScene::init(Root *root, RenderWindow *win,
		OgreBulletApplication *application) {

	// ------------------------
	// Start OgreScene
	mSceneMgr = root->createSceneManager(ST_GENERIC);

	mCamera = mSceneMgr->createCamera("Cam");
	//mCamera->setFOVy(Degree(90));
	mCamera->setNearClipDistance(0.1);
	mCamera->setFarClipDistance(1000);
	Viewport *vp = win->addViewport(mCamera);
	vp->setBackgroundColour(ColourValue(0, 0, 0));
	// Alter the camera aspect ratio to match the viewport
	mCamera->setAspectRatio(
			Real(vp->getActualWidth()) / Real(vp->getActualHeight()));

	mCamera->setPosition(Ogre::Vector3(10, 50, 0));
	mCamera->lookAt(0, 0, 0);

	//mCamera->rotate(Ogre::Vector3(1,0,0), Degree(90));
	//mCamera->setFixedYawAxis(true, Ogre::Vector3::UNIT_Z);

	OgreBulletListener::init(root, win, application);

	// ------------------------
	// add lights
	setBasicLight();
	// ------------------------
	// Add the Gui
	setPhysicGUI();
	// ------------------------
	// Start Bullet
	initWorld(Ogre::Vector3(0,0,0));
	// ------------------------
	// Add the ground
//	addGround();

}
// -------------------------------------------------------------------------
void StemLifeScene::keyPressed(BULLET_KEY_CODE key) {
	//    OgreBulletListener::throwDynamicObject (key);
	//    OgreBulletListener::dropDynamicObject (key);

	return OgreBulletListener::keyPressed(key);
}

RigidBody *StemLifeScene::addFood(const String instanceName,
		const Ogre::Vector3 &pos, const Quaternion &q, const Real &radius,
		const Real bodyRestitution, const Real bodyFriction,
		const Real bodyMass) {
	Entity *entity = mSceneMgr->createEntity(
			"food" + StringConverter::toString(mNumEntitiesInstanced),
			"ellipsoid.mesh");
	// "Crate.mesh");
	// "Crate1.mesh");
	// "Crate2.mesh");


	entity->setQueryFlags(GEOMETRY_QUERY_MASK);
#if (OGRE_VERSION < ((1 << 16) | (5 << 8) | 0)) // only applicable before shoggoth (1.5.0)
	entity->setNormaliseNormals(true);
#endif
	entity->setCastShadows(true);

	entity->setMaterialName("Food");

	SphereCollisionShape *sceneCubeShape = new SphereCollisionShape(radius);

	BioEntity *food = new BioEntity(
			"food" + StringConverter::toString(mNumEntitiesInstanced), mWorld);
	SceneNode *node = mSceneMgr->getRootSceneNode()->createChildSceneNode();
	node->attachObject(entity);

	food->setShape(node, sceneCubeShape, bodyRestitution, bodyFriction,
			bodyMass, pos, q);
	food->getBulletObject()->setUserPointer(food);

	mEntities.push_back(entity);
	mShapes.push_back(sceneCubeShape);
	mBodies.push_back(food);
	bodyIndex[food] = mBodies.size() - 1;
	bioEntities.insert(food);
	kd_insert3(kdFood, food->getWorldPosition().x, food->getWorldPosition().y,
			food->getWorldPosition().z, food);
	mNumEntitiesInstanced++;

	return food;
}
// -------------------------------------------------------------------------
Cell *StemLifeScene::addCell(const String instanceName,
		const Ogre::Vector3 &pos, const Quaternion &q, const Real radius,
		const Real bodyRestitution, const Real bodyFriction,
		const Real bodyMass) {
	Entity *entity = mSceneMgr->createEntity(
			instanceName + StringConverter::toString(mNumEntitiesInstanced),
			"ellipsoid.mesh");

	entity->setQueryFlags(GEOMETRY_QUERY_MASK);
#if (OGRE_VERSION < ((1 << 16) | (5 << 8) | 0)) // only applicable before shoggoth (1.5.0)
	entity->setNormaliseNormals(true);
#endif
	entity->setCastShadows(true);

	entity->setMaterialName("Cell");

	SphereCollisionShape *sceneCubeShape = new SphereCollisionShape(radius);

	Cell *cell = new Cell(
			"cell" + StringConverter::toString(
					mNumEntitiesInstanced), mWorld, kdFood);

	SceneNode *node = mSceneMgr->getRootSceneNode()->createChildSceneNode();
	node->attachObject(entity);

	cell->setShape(node, sceneCubeShape, bodyRestitution, bodyFriction,
			bodyMass, pos, q);
	cell->getBulletObject()->setUserPointer(cell);

	mEntities.push_back(entity);
	mShapes.push_back(sceneCubeShape);
	mBodies.push_back(cell);
	bodyIndex[cell] = mBodies.size() - 1;
	bioEntities.insert(cell);
	mNumEntitiesInstanced++;

	return cell;
}

void StemLifeScene::initWorld(const Ogre::Vector3 &gravityVector,
		const AxisAlignedBox &bounds) {
	// Start Bullet
	mWorld = new StemLifeWorld(mSceneMgr, bounds, gravityVector, true, true,
			10000);
	kdFood = kd_create(3);
	// add Debug info display tool
	DebugDrawer *debugDrawer = new DebugDrawer();

	mWorld->setDebugDrawer(debugDrawer);

	SceneNode *node = mSceneMgr->getRootSceneNode()->createChildSceneNode(
			"debugDrawer", Ogre::Vector3::ZERO);
	node->attachObject(static_cast<SimpleRenderable *> (debugDrawer));

	// Creates food
	for (Real i = 0; i < 1; i++) {
		for (Real j = 0; j < 1; j++) {
			Real x = Math::RangeRandom(-50, 50);
			Real z = Math::RangeRandom(-50, 50);
			Ogre::Vector3 vec(x, 1, z);

			OgreBulletDynamics::RigidBody *body = addFood("food", vec,
					Quaternion(0, 0, 0, 1), gSphereBodyBounds, 1e-100,
					gDynamicBodyFriction, 1e100);

		}
	}

	// Creates cells
	for (Real i = 0; i < 1; i++) {
		for (Real j = 0; j < 1; j++) {
			Real x = Math::RangeRandom(-50, 50);
			Real z = Math::RangeRandom(-50, 50);
			Ogre::Vector3 vec(x, 1, z);
			Cell *body = addCell("cell", vec, Quaternion(0, 0, 0, 1),
					gSphereBodyBounds, gDynamicBodyRestitution,
					gDynamicBodyFriction, gDynamicBodyMass);
		}
	}
}

bool StemLifeScene::frameStarted(Real elapsedTime) {
	if (mQuit)
		return false;

	if (mInputListener->getButton2Pressed()) {
		mCamera->yaw(mCameraRotX);
		mCamera->pitch(mCameraRotY);

		mCameraRotX = 0;
		mCameraRotY = 0;
	}

	mCamera->moveRelative(mCameraTrans);

	if (!mPaused || mDoOnestep) {
		// Update bio simulation
		for (std::set<BioEntity*>::iterator be = bioEntities.begin(); be != bioEntities.end(); be++) {
			(*be)->stepBioSimulation(elapsedTime);
		}

		// update physics
		mWorld->stepSimulation(elapsedTime);

		// Process collisions between bio objects
		processBioCollisions();
	}
	mDoOnestep = false;

	return true;
}

void StemLifeScene::processBioCollisions() {
	//Assume world->stepSimulation or world->performDiscreteCollisionDetection has been called

	int numManifolds = mWorld->getBulletDynamicsWorld()->getDispatcher()->getNumManifolds();
	for (int i=0;i<numManifolds;i++)
	{
		btPersistentManifold* contactManifold =  mWorld->getBulletDynamicsWorld()->getDispatcher()->getManifoldByIndexInternal(i);

		if (contactManifold->getNumContacts() > 0) {
			btCollisionObject* obA = static_cast<btCollisionObject*>(contactManifold->getBody0());
			btCollisionObject* obB = static_cast<btCollisionObject*>(contactManifold->getBody1());
			cout << "Collision: " << obA->getUserPointer() << "  " << obB->getUserPointer() << endl;

			if ((obA->getUserPointer() != 0) && (obB->getUserPointer() != 0) ) {
				// Only BioEntities are set as user pointers in btCollisionObjects
				BioEntity* be1 = (BioEntity*)obA->getUserPointer();
				BioEntity* be2 =(BioEntity*)obB->getUserPointer();
				be1->collideWith(be2);
				be2->collideWith(be1);

			}
		}
	}

}

void StemLifeScene::remove(BioEntity* be) {
	int i = bodyIndex[be];

	Entity *entity = mEntities[i];

	SceneNode* node = (SceneNode*)entity->getParentNode();

	node->detachAllObjects();

	mSceneMgr->getRootSceneNode()->removeChild(node);

	delete entity;
	delete node;

	CollisionShape *shape = mShapes[i];





	mEntities.erase(mEntities.begin() + i);
	mShapes.erase(mShapes.begin() + i);
	mBodies.erase(mBodies.begin() + i);
	bioEntities.erase(be);

	delete be;
	delete shape;

	mNumEntitiesInstanced--;


}
